package service

import (
	"errors"
	"fmt"
	ClickHouse "gin/database"
	"gin/model"
	"gin/types"
	"github.com/gin-gonic/gin"
	"github.com/shopspring/decimal"
	"gorm.io/gorm"
	"sort"
	"strings"
	"time"
)

type Finance struct {
}

type AssetEntityWithGrade struct {
	model.Entity
	AssetGrade string `json:"asset_grade"`
}

var dingtalkService DingTalk

var gradeProp = map[string]decimal.Decimal{"资深": decimal.NewFromFloat(7), "专家": decimal.NewFromFloat(4.50), "高级": decimal.NewFromFloat(3), "中级": decimal.NewFromFloat(2), "初级": decimal.NewFromFloat(1)}
var gradeList = []string{"资深", "专家", "高级", "中级", "初级"}

// FinanceHourCount 财务工时统计
func (f Finance) FinanceHourCount(tenantId int64, month string, projectIdList []uint64, episodeList []types.EpisodeItem) (res []types.FinanceHourCountResItem, err error) {

	monthTime, err := time.ParseInLocation("2006-01", month, time.Local)
	if err != nil {
		return
	}
	from := GetFirstDayOfMonth(monthTime)
	to := GetLastDayOfMonth(monthTime)
	var monthFilter = []int64{from.Unix(), to.Unix()}
	// 根据项目id获取项目对应月份计划
	var doneEpisodeList []types.PlanEpisodeItem
	err = ClickHouse.DB.Table("plan p").Joins("JOIN entity e on e.id = p.entity_id").
		Where("p.tenant_id = ? and p.type = 'episode' and p.end_time between ? and ?", tenantId, monthFilter[0], monthFilter[1]).
		Group("e.project_id,e.code").
		Select("e.project_id as project_id,arrayStringConcat(groupUniqArray(e.code),',') as episode_code_str").
		Find(&doneEpisodeList).Error

	if err != nil {
		return
	}
	if len(doneEpisodeList) == 0 {
		return
	}

	// 查询出部门信息
	var departmentService Department
	departmentList, err := departmentService.getDepartmentList(tenantId, nil)
	if err != nil {
		return
	}
	// 资产和镜头分开计算工时信息
	assetDepartmentUserCount, err := f.GetAssetUserDepartmentManhourCount(doneEpisodeList, projectIdList, episodeList)
	if err != nil {
		return
	}
	shotDepartmentUserCount, err := f.GetShotUserDepartmentManhourCount(doneEpisodeList, projectIdList, episodeList)
	if err != nil {
		return
	}
	// 通过唯一键 进行资产和镜头的数据合并
	var checkKey string
	resMap := assetDepartmentUserCount
	for _, item := range shotDepartmentUserCount {
		checkKey = fmt.Sprintf("%d#%s#%d#%d", item.ProjectID, item.InitialEpisodeCode, item.ParentID, item.UserID)
		if _, ok := resMap[checkKey]; ok {
			resMap[checkKey] = types.FinanceHourCountResItem{
				ProjectID:          item.ProjectID,
				InitialEpisodeCode: item.InitialEpisodeCode,
				Hours:              item.Hours + resMap[checkKey].Hours,
				UserID:             item.UserID,
				ID:                 item.ID,
				Type:               item.Type,
				Name:               item.Name,
				ProjectName:        item.ProjectName,
				ParentID:           item.ParentID,
				Percent:            item.Percent,
				DepartmentHour:     item.DepartmentHour,
			}
		} else {
			resMap[checkKey] = item
		}
	}

	if len(resMap) == 0 {
		return
	}
	// 遍历一次  并且计算每个人的工时总量
	var projectIdMap = make(map[uint64]uint64, 0)
	var userUnionIdMap = make(map[uint64]uint64, 0)
	var userManhourMap = make(map[uint64]float64, 0)
	for _, re := range resMap {
		projectIdMap[re.ProjectID] = re.ProjectID
		userUnionIdMap[re.UserID] = re.UserID
		if _, ok := userManhourMap[re.UserID]; !ok {
			userManhourMap[re.UserID] = re.Hours
		} else {
			userManhourMap[re.UserID] = userManhourMap[re.UserID] + re.Hours
		}
		res = append(res, re)
	}

	var userIdList = make([]uint64, 0)
	for u := range userUnionIdMap {
		userIdList = append(userIdList, u)
	}
	var userList = make([]UserManHourUser, len(userIdList))

	err = ClickHouse.DB.Model(&model.User{}).
		Where("union_id in ?", userIdList).Select("union_id as id,name,phone,email,avatar").Find(&userList).Error
	if err != nil {
		return
	}
	var userMap = make(map[uint64]UserManHourUser)
	for _, user := range userList {
		userMap[user.Id] = user
	}

	var projectIdListForProjectList = make([]uint64, 0)
	for p := range projectIdMap {
		projectIdListForProjectList = append(projectIdListForProjectList, p)
	}
	var projectList = make([]model.Project, 0)
	err = ClickHouse.DB.Model(&model.Project{}).Where("id in ?", projectIdListForProjectList).Find(&projectList).Error
	if err != nil {
		return
	}
	var projectMap = make(map[uint64]model.Project)
	for _, m := range projectList {
		projectMap[m.Id] = m
	}

	departmentUserDingHour, err := dingtalkService.GetDepartmentUserManhourM(tenantId, from, to)
	if err != nil {
		return
	}
	// 填充项目,用户信息 计算实际工时
	var manhourKey = ""
	for i, re := range res {
		if _, ok := userMap[re.UserID]; ok {
			res[i].Name = userMap[re.UserID].Name
		}
		if _, ok := projectMap[re.ProjectID]; ok {
			res[i].ProjectName = projectMap[re.ProjectID].Name
		}
		manhourKey = fmt.Sprintf("%d#%d", re.ParentID, re.UserID)
		if _, ok := departmentUserDingHour[manhourKey]; ok {
			res[i].DepartmentHourTotal = Decimal(departmentUserDingHour[manhourKey].ManHourM / 60)
			if re.Hours > 0 {
				res[i].DepartmentHour = Decimal(res[i].DepartmentHourTotal * (re.Hours / userManhourMap[re.UserID]))
			}
		}
		if _, ok := userManhourMap[re.UserID]; ok {
			res[i].HoursTotal = userManhourMap[re.UserID]
		}
	}

	// 最后把部门信息追加到结果中
	for _, department := range departmentList {
		res = append(res, types.FinanceHourCountResItem{
			ProjectID:          0,
			InitialEpisodeCode: "",
			Hours:              0.00,
			UserID:             0,
			ID:                 department.Id,
			Type:               "department",
			Name:               department.Name,
			ProjectName:        "",
			ParentID:           department.ParentId,
			Percent:            0,
			DepartmentHour:     0,
		})

	}

	return
}

// GetAssetUserDepartmentManhourCount 查询资产部门用户工时统计
func (f *Finance) GetAssetUserDepartmentManhourCount(doneEpisodeList []types.PlanEpisodeItem, projectIdList []uint64, episodeList []types.EpisodeItem) (res map[string]types.FinanceHourCountResItem, err error) {

	// 查询出这些集要完成的资产和关卡
	var assetLevelIdList []uint64
	db := ClickHouse.DB.Table("entity e").Where("e.module_id = (select id from module where code = 'shot')")
	if len(projectIdList) > 0 {
		db = db.Where("e.project_id in ?", projectIdList)
	}
	if len(doneEpisodeList) > 0 {
		for _, item := range doneEpisodeList {
			db.Where("e.project_id = ? and e.initial_episode_code in (?)", item.ProjectId, item.EpisodeCodeStr)
		}
	}

	if len(episodeList) > 0 {
		var dbEpisodeWhere = ClickHouse.DB
		for _, item := range episodeList {
			dbEpisodeWhere = dbEpisodeWhere.Or("e.project_id = ? and e.initial_episode_code in ?", item.ProjectId, item.EpisodeCodeList)
		}
		db = db.Where(dbEpisodeWhere)
	}
	err = db.Select("arrayJoin(arrayConcat(" +
		"splitByChar(',', assumeNotNull(JSONExtractString(e.json, 'shot_asset')))," +
		"splitByChar(',',assumeNotNull(JSONExtractString(e.json, 'shot_level'))))" +
		") as asset_id").
		Distinct().
		Having("toUInt64OrZero(asset_id) > 0 ").
		Find(&assetLevelIdList).Error
	if err != nil || len(assetLevelIdList) == 0 {
		return
	}

	var assetLevelRes []types.FinanceHourCountResItem
	// 计算资产和关卡工时
	db = ClickHouse.DB.Model(&model.Entity{}).
		Joins("JOIN task on task.entity_id = entity.id").
		Joins("JOIN user on user.id = task.executor").
		Where("entity.id in ?", assetLevelIdList).
		Where("task.step_type = 'step'").
		Where("task.department_id > 0")

	err = db.Group("entity.project_id,entity.initial_episode_code,task.department_id,user.union_id").
		Select("entity.project_id AS project_id,entity.initial_episode_code AS initial_episode_code,task.department_id AS parent_id,sum(toFloat64OrZero(task.man_hour)) AS hours,user.union_id AS user_id,'user' as type").
		Find(&assetLevelRes).Error
	if err != nil {
		return
	}
	// 存在检查map
	var userMap = make(map[string]types.FinanceHourCountResItem, 0)
	for _, re := range assetLevelRes {
		userMap[fmt.Sprintf("%d#%s#%d#%d", re.ProjectID, re.InitialEpisodeCode, re.ParentID, re.UserID)] = re
	}

	// 查询出概念id
	var designIdList []uint64
	err = ClickHouse.DB.Model(&model.Module{}).Joins("JOIN entity on entity.module_id = module.id").
		Where("entity.id in ?", assetLevelIdList).
		Select("JSONExtract(entity.json, concat(module.code, '_design'),'UInt32') as design_id").Pluck("design_id", &designIdList).Error
	if err != nil {
		return
	}
	// 计算资产概念和关卡概念工时
	var designRes []types.FinanceHourCountResItem
	err = ClickHouse.DB.Model(&model.Entity{}).
		Joins("JOIN task on task.entity_id = entity.id").
		Joins("JOIN user on user.id = task.executor").
		Where("entity.id in ?", designIdList).
		Where("task.step_type = 'step' and task.department_id > 0").
		Group("entity.project_id,entity.initial_episode_code,task.department_id,user.union_id").
		Select("entity.project_id AS project_id,entity.initial_episode_code AS initial_episode_code,task.department_id AS parent_id,sum(toFloat64OrZero(task.man_hour)) AS hours,user.union_id AS user_id,'user' as type").
		Find(&designRes).Error
	if err != nil {
		return
	}
	if len(designRes) == 0 {
		return
	}
	var checkKey string
	for _, re := range designRes {
		checkKey = fmt.Sprintf("%d#%s#%d#%d", re.ProjectID, re.InitialEpisodeCode, re.ParentID, re.UserID)
		re.Hours = Decimal(re.Hours)
		if _, ok := userMap[checkKey]; ok {
			userMap[checkKey] = types.FinanceHourCountResItem{
				ProjectID:          re.ProjectID,
				InitialEpisodeCode: re.InitialEpisodeCode,
				Hours:              re.Hours + userMap[checkKey].Hours,
				UserID:             re.UserID,
				ID:                 re.ID,
				Type:               re.Type,
				Name:               re.Name,
				ProjectName:        re.ProjectName,
				ParentID:           re.ParentID,
				Percent:            re.Percent,
				DepartmentHour:     re.DepartmentHour,
			}
		} else {
			userMap[checkKey] = re
		}
	}

	return userMap, err
}

// GetAssetUserDepartmentManhourCountByEpisodeStepCategory 查询资产部门用户工时统计
func (f *Finance) GetAssetUserDepartmentManhourCountByEpisodeStepCategory(doneEpisodeList []types.PlanEpisodeStepCategoryItem, projectIdList []uint64, episodeList []types.EpisodeItem) (res map[string]types.FinanceHourCountResItem, err error) {

	// 查询出这些集要完成的资产和关卡
	var assetLevelIdList []uint64
	db := ClickHouse.DB.Table("entity e").Where("e.module_id = (select id from module where code = 'shot')")
	if len(projectIdList) > 0 {
		db = db.Where("e.project_id in ?", projectIdList)
	}
	if len(doneEpisodeList) > 0 {
		for _, item := range doneEpisodeList {
			db.Where("e.project_id = ? and e.initial_episode_code in (?)", item.ProjectId, item.EpisodeCodeStr)
		}
	}

	if len(episodeList) > 0 {
		var dbEpisodeWhere = ClickHouse.DB
		for _, item := range episodeList {
			dbEpisodeWhere = dbEpisodeWhere.Or("e.project_id = ? and e.initial_episode_code in ?", item.ProjectId, item.EpisodeCodeList)
		}
		db = db.Where(dbEpisodeWhere)
	}
	err = db.Select("arrayJoin(arrayConcat(" +
		"splitByChar(',', assumeNotNull(JSONExtractString(e.json, 'shot_asset')))," +
		"splitByChar(',',assumeNotNull(JSONExtractString(e.json, 'shot_level'))))" +
		") as asset_id").
		Distinct().
		Having("toUInt64OrZero(asset_id) > 0 ").
		Find(&assetLevelIdList).Error
	if err != nil || len(assetLevelIdList) == 0 {
		return
	}

	var assetLevelRes []types.FinanceHourCountResItem
	// 计算资产和关卡工时
	db = ClickHouse.DB.Model(&model.Entity{}).
		Joins("JOIN task on task.entity_id = entity.id").
		Joins("JOIN user on user.id = task.executor").
		Where("entity.id in ?", assetLevelIdList).
		Where("task.step_type = 'step'").
		Where("task.department_id > 0")

	err = db.Group("entity.project_id,entity.initial_episode_code,task.department_id,user.union_id").
		Select("entity.project_id AS project_id,entity.initial_episode_code AS initial_episode_code,task.department_id AS parent_id,sum(toFloat64OrZero(task.man_hour)) AS hours,user.union_id AS user_id,'user' as type").
		Find(&assetLevelRes).Error
	if err != nil {
		return
	}
	// 存在检查map
	var userMap = make(map[string]types.FinanceHourCountResItem, 0)
	for _, re := range assetLevelRes {
		userMap[fmt.Sprintf("%d#%s#%d#%d", re.ProjectID, re.InitialEpisodeCode, re.ParentID, re.UserID)] = re
	}

	// 查询出概念id
	var designIdList []uint64
	err = ClickHouse.DB.Model(&model.Module{}).Joins("JOIN entity on entity.module_id = module.id").
		Where("entity.id in ?", assetLevelIdList).
		Select("JSONExtract(entity.json, concat(module.code, '_design'),'UInt32') as design_id").Pluck("design_id", &designIdList).Error
	if err != nil {
		return
	}
	// 计算资产概念和关卡概念工时
	var designRes []types.FinanceHourCountResItem
	err = ClickHouse.DB.Model(&model.Entity{}).
		Joins("JOIN task on task.entity_id = entity.id").
		Joins("JOIN user on user.id = task.executor").
		Where("entity.id in ?", designIdList).
		Where("task.step_type = 'step' and task.department_id > 0").
		Group("entity.project_id,entity.initial_episode_code,task.department_id,user.union_id").
		Select("entity.project_id AS project_id,entity.initial_episode_code AS initial_episode_code,task.department_id AS parent_id,sum(toFloat64OrZero(task.man_hour)) AS hours,user.union_id AS user_id,'user' as type").
		Find(&designRes).Error
	if err != nil {
		return
	}
	if len(designRes) == 0 {
		return
	}
	var checkKey string
	for _, re := range designRes {
		checkKey = fmt.Sprintf("%d#%s#%d#%d", re.ProjectID, re.InitialEpisodeCode, re.ParentID, re.UserID)
		re.Hours = Decimal(re.Hours)
		if _, ok := userMap[checkKey]; ok {
			userMap[checkKey] = types.FinanceHourCountResItem{
				ProjectID:          re.ProjectID,
				InitialEpisodeCode: re.InitialEpisodeCode,
				Hours:              re.Hours + userMap[checkKey].Hours,
				UserID:             re.UserID,
				ID:                 re.ID,
				Type:               re.Type,
				Name:               re.Name,
				ProjectName:        re.ProjectName,
				ParentID:           re.ParentID,
				Percent:            re.Percent,
				DepartmentHour:     re.DepartmentHour,
			}
		} else {
			userMap[checkKey] = re
		}
	}

	return userMap, err
}

// GetShotUserDepartmentManhourCount 查询镜头部门用户工时统计
func (f *Finance) GetShotUserDepartmentManhourCount(doneEpisodeList []types.PlanEpisodeItem, projectIdList []uint64, episodeList []types.EpisodeItem) (res []types.FinanceHourCountResItem, err error) {
	db := ClickHouse.DB.Model(&model.Entity{}).Joins("JOIN task on task.entity_id = entity.id").Joins("JOIN user on user.id = task.executor").
		Where("entity.module_id in (select id from module where code in ('shot','sequence'))").
		Where("task.step_type = 'step'").
		Where("task.department_id > 0")

	if len(projectIdList) > 0 {
		db = db.Where("entity.project_id in ?", projectIdList)
	}
	if len(doneEpisodeList) > 0 {
		for _, item := range doneEpisodeList {
			db.Where("entity.project_id = ? and entity.initial_episode_code in ?", item.ProjectId, strings.Split(item.EpisodeCodeStr, ","))
		}
	}

	if len(episodeList) > 0 {
		var dbEpisodeWhere = ClickHouse.DB
		for _, item := range episodeList {
			dbEpisodeWhere = dbEpisodeWhere.Or("entity.project_id = ? and entity.initial_episode_code in ?", item.ProjectId, item.EpisodeCodeList)
		}
		db = db.Where(dbEpisodeWhere)
	}
	err = db.Group("entity.project_id,entity.initial_episode_code,task.department_id,user.union_id").
		Select("entity.project_id AS project_id,entity.initial_episode_code AS initial_episode_code,task.department_id AS parent_id,sum(toFloat64OrZero(task.man_hour)) AS hours,user.union_id AS user_id,'user' as type").
		Find(&res).Error
	if err != nil {
		return
	}
	for i, _ := range res {
		res[i].Hours = Decimal(res[i].Hours)
	}
	return
}

// GetEpisodeStepCategoryByPlan 查询计划要完成的环节的集
func (f *Finance) GetEpisodeStepCategoryByPlan(where interface{}) (res []types.PlanEpisodeStepCategoryItem, err error) {
	err = ClickHouse.DB.Table("plan p").
		Joins("JOIN entity e on e.id = p.entity_id").
		Where(where).
		Group("p.project_id,p.step_category_id").
		Select("p.project_id as project_id,p.step_category_id as step_category_id, arrayStringConcat(groupUniqArray(e.code), ',') as episode_code_str").
		Find(&res).Error
	return
}

// GetModulesStepCategory 通过模块code获取分类信息
func (f *Finance) GetModulesStepCategory(moduleCodeList []string) (res []model.StepCategory, err error) {
	err = ClickHouse.DB.Table("step_category sc").
		Joins("join step_category scc on sc.id = scc.parent_id").
		Where("sc.entity_id in (select id from module where code in ?)", moduleCodeList).
		Select("scc.id AS id").
		Find(&res).Error
	return
}

// GetAssetDataMap 获得实体数据map
func (f *Finance) GetAssetDataMap(where interface{}) (res map[uint64]AssetEntityWithGrade, err error) {
	// 查询实体信息
	var assetEntityDataList []AssetEntityWithGrade
	err = ClickHouse.DB.Model(&model.Entity{}).Joins("JOIN module on module.id = entity.module_id").
		Where(where).
		Select("id,name,JSONExtractString(entity.json, concat(module.code, '_grade')) AS asset_grade").
		Find(&assetEntityDataList).Error
	if err != nil {
		return
	}
	res = make(map[uint64]AssetEntityWithGrade, 0)
	for _, entity := range assetEntityDataList {
		res[entity.Id] = entity
	}
	return
}

// getWorkhourProp 获得等级工时系数
func (f *Finance) getWorkhourProp(grade string) decimal.Decimal {
	workhourProp := decimal.NewFromInt(1)
	if _, ok := gradeProp[grade]; ok {
		// 人员等级系数
		workhourProp = gradeProp[grade]
	}
	return workhourProp
}

// FinanceAssetHourCount
func (f *Finance) FinanceAssetHourCount(tenantId uint64, TaskType string, month string, projectIdList []uint64, episodeList []types.EpisodeItem) (res types.FinanceAssetHourCountRes, err error) {

	monthTime, err := time.ParseInLocation("2006-01", month, time.Local)
	if err != nil {
		return
	}
	from := GetFirstDayOfMonth(monthTime)
	to := GetLastDayOfMonth(monthTime)
	// 通过时间 获取项目计划
	var moduleCodeList []string
	if TaskType == "asset" {
		moduleCodeList = []string{"level", "asset", "design"}
	} else {
		moduleCodeList = []string{"shot", "sequence"}
	}
	assetStepCategory, err := f.GetModulesStepCategory(moduleCodeList)
	if err != nil {
		return
	}
	var assetStepCategoryIdList []uint64
	for _, category := range assetStepCategory {
		assetStepCategoryIdList = append(assetStepCategoryIdList, category.Id)
	}

	var assetWhere = ClickHouse.DB.Where("task.plan_end_time > ? and task.plan_start_time < ? and task.step_category_id in ?", from.Unix(), to.Unix(), assetStepCategoryIdList)

	if len(projectIdList) > 0 {
		assetWhere = assetWhere.Where("entity.project_id in ?", projectIdList)
	}

	if len(episodeList) > 0 {
		var assetEpisodeWhere = ClickHouse.DB
		for _, item := range episodeList {
			assetEpisodeWhere.Or("entity.project_id = ? and entity.initial_episode_code in ?", item.ProjectId, item.EpisodeCodeList)
		}
		assetWhere = assetWhere.Where(assetEpisodeWhere)
	}

	// 计算资产工时
	var assetDepartmentRes []types.EntityDepartmentStepCategoryHourCount
	err = ClickHouse.DB.Model(&model.Module{}).
		Joins("JOIN entity on entity.module_id = module.id").
		Joins("JOIN task on task.entity_id = entity.id").
		Joins("JOIN user on user.id = task.executor").
		Where(assetWhere).
		Where("task.step_type = 'step' and task.department_id > 0").
		Group("entity.project_id,entity.id,entity.initial_episode_code,task.step_category_id").
		Select("entity.project_id AS project_id,entity.id as entity_id,entity.initial_episode_code AS initial_episode_code,task.step_category_id as step_category_id,sum(toUInt64OrZero(toString(toFloat64OrZero(task.man_hour) * 100)))/100 AS hours").
		Having("hours > 0").
		Find(&assetDepartmentRes).Error
	if err != nil {
		return
	}
	if len(assetDepartmentRes) == 0 {
		return
	}

	var assetResMap = make(map[string]types.EntityDepartmentHourCount, 0)
	var planProportion = decimal.NewFromFloat(0.00)
	var entityStepCategoryKey = ""
	var assetIdMap = make(map[uint64]uint64, 0)
	var assetIdList []uint64
	var projectIdMap = make(map[uint64]uint64, 0)
	var projectIdListForQuery []uint64
	var stepCategoryIdList []uint64
	var stepCategoryIdMap = make(map[uint64]uint64)
	for _, re := range assetDepartmentRes {
		if _, ok := assetIdMap[re.EntityId]; !ok {
			assetIdList = append(assetIdList, re.EntityId)
			assetIdMap[re.EntityId] = re.EntityId
		}
		if _, ok := projectIdMap[re.ProjectId]; !ok {
			projectIdListForQuery = append(projectIdListForQuery, re.ProjectId)
			projectIdMap[re.ProjectId] = re.ProjectId
		}
		if _, ok := stepCategoryIdMap[re.StepCategoryId]; !ok {
			stepCategoryIdList = append(stepCategoryIdList, re.StepCategoryId)
			stepCategoryIdMap[re.StepCategoryId] = re.StepCategoryId
		}

		entityStepCategoryKey = fmt.Sprintf("%d#%d", re.StepCategoryId, re.EntityId)
		planProportion = decimal.NewFromInt(1)
		//if _, ok := planTimeProportion[fmt.Sprintf("%s#%d", re.InitialEpisodeCode, re.StepCategoryId)]; ok {
		//	planProportion = planTimeProportion[fmt.Sprintf("%s#%d", re.InitialEpisodeCode, re.StepCategoryId)]
		//}
		if _, ok := assetResMap[entityStepCategoryKey]; !ok {
			assetResMap[entityStepCategoryKey] = types.EntityDepartmentHourCount{
				ProjectId:          re.ProjectId,
				StepCategoryId:     re.StepCategoryId,
				InitialEpisodeCode: re.InitialEpisodeCode,
				EntityName:         re.EntityName,
				Grade:              re.Grade,
				EntityId:           re.EntityId,
				Hours:              re.Hours,
				HoursDecimal:       decimal.NewFromFloat(re.Hours).Mul(planProportion),
				Proportion:         planProportion,
			}
		} else {
			assetResMap[entityStepCategoryKey] = types.EntityDepartmentHourCount{
				ProjectId:          re.ProjectId,
				StepCategoryId:     re.StepCategoryId,
				InitialEpisodeCode: re.InitialEpisodeCode,
				EntityName:         re.EntityName,
				Grade:              re.Grade,
				EntityId:           re.EntityId,
				Hours:              re.Hours + assetResMap[entityStepCategoryKey].Hours,
				HoursDecimal:       decimal.NewFromFloat(re.Hours).Mul(planProportion).Add(assetResMap[entityStepCategoryKey].HoursDecimal),
				Proportion:         planProportion,
			}
		}
	}

	// 查询实体信息
	entityDataMap, err := f.GetAssetDataMap(ClickHouse.DB.Where("entity.id in ?", assetIdList))
	if err != nil {
		return
	}
	// 查询项目信息
	var projectService = Project{}
	projectData, err := projectService.GetProjectNameMapByIds(projectIdListForQuery)
	if err != nil {
		return
	}
	var assetEpisodeRelation types.AssetRelatedEpisodeListRes
	if TaskType == "asset" {
		// 查询资产出现的集
		assetEpisodeRelation, err = f.GetAssetRelatedEpisodeList(assetIdList)
		if err != nil {
			return
		}
	}

	var standardHours decimal.Decimal
	departmentIdMap := make(map[uint64]uint64, 0)
	var departmentIdList []uint64

	// 字典转数组 字典是没办法修改字典element里数据的 因为map里的element 如果为map型是不可寻址的
	var assetRes types.EntityDepartmentHourCountList
	for _, assetCountItem := range assetResMap {
		standardHours = standardHours.Add(assetCountItem.HoursDecimal)
		if _, ok := departmentIdMap[assetCountItem.DepartmentId]; !ok {
			departmentIdList = append(departmentIdList, assetCountItem.DepartmentId)
		}
		departmentIdMap[assetCountItem.DepartmentId] = assetCountItem.DepartmentId
		if TaskType == "asset" {
			if _, ok := assetEpisodeRelation[assetCountItem.EntityId]; ok {
				assetCountItem.RelatedEpisodeList = strings.Split(assetEpisodeRelation[assetCountItem.EntityId].EpisodeCodeStr, ",")
			}
		}
		if _, ok := entityDataMap[assetCountItem.EntityId]; ok {
			assetCountItem.EntityName = entityDataMap[assetCountItem.EntityId].Name
			assetCountItem.Grade = entityDataMap[assetCountItem.EntityId].AssetGrade
		}
		if _, ok := projectData[assetCountItem.ProjectId]; ok {
			assetCountItem.ProjectName = projectData[assetCountItem.ProjectId]
		}

		assetRes = append(assetRes, assetCountItem)
	}

	// 按照等级排序
	sort.Sort(assetRes)

	var userMonthWorkhourService UserMonthWorkhour
	var workHourWhere = ClickHouse.DB.Where("umw.department_name <> '导演' and umw.is_leave = 'no' and tu.tenant_id = ? and umw.work_date between ? and ? and r.code in ?", tenantId, from.Unix(), to.Unix(), []string{"headman", "artist"})
	DepartmentManhourList, err := userMonthWorkhourService.GetMonthManhour(workHourWhere, stepCategoryIdList)
	if err != nil {
		return
	}
	// 离职人员工时查询
	LeavePeopleDepartmentManhourList, err := userMonthWorkhourService.GetMonthManhourLeavePeople(ClickHouse.DB.Where("umw.department_name <> '导演' and umw.is_leave = 'yes' and umw.work_date between ? and ? ", from.Unix(), to.Unix()), stepCategoryIdList)
	if err != nil {
		return
	}
	if len(LeavePeopleDepartmentManhourList) > 0 {
		DepartmentManhourList = append(DepartmentManhourList, LeavePeopleDepartmentManhourList...)
	}

	DepartmentManhourMap := make(map[string][]*types.DepartmentUserDingManhour)
	userWorkhourMap := make(map[string]decimal.Decimal)
	workHourTotal := decimal.NewFromInt(0)

	for i, manhour := range DepartmentManhourList {
		workhourProp := f.getWorkhourProp(manhour.Grade)
		DepartmentManhourList[i].HoursDecimal = decimal.NewFromFloat(manhour.Hours).Mul(workhourProp)
		workHourTotal = workHourTotal.Add(DepartmentManhourList[i].HoursDecimal)
		gradeStepCategoryKey := fmt.Sprintf("%d#%s", manhour.StepCategoryId, manhour.Grade)
		DepartmentManhourMap[gradeStepCategoryKey] = append(DepartmentManhourMap[gradeStepCategoryKey], &DepartmentManhourList[i])
		userWorkhourMap[manhour.UserName] = DepartmentManhourList[i].HoursDecimal
	}

	gradeStepCategoryKey := ""
	var tempAssetHour decimal.Decimal
	hourProportion := workHourTotal.DivRound(standardHours, 2) // 工时比例
	emptyDecimal := decimal.NewFromInt(0)
	resMap := make(map[string]types.FinanceAssetHourCountResItem, 0)
	for _, assetCount := range assetRes {
		if assetCount.HoursDecimal.Equal(emptyDecimal) {
			//println("资产工时为0")
			continue
		}
		tempAssetHour = assetCount.HoursDecimal.Mul(hourProportion)
		var tempDecrease float64
		for _, grade := range gradeList {
			gradeStepCategoryKey = fmt.Sprintf("%d#%s", assetCount.StepCategoryId, grade)
			if _, ok := DepartmentManhourMap[gradeStepCategoryKey]; !ok {
				// 找不到对应等级的出勤人员
				//println("找不到对应等级的出勤人员", gradeStepCategoryKey)
				continue
			}
			workhourProp := f.getWorkhourProp(grade)
			// 找到对应等级人员 查看工时是否足够
			for i := range DepartmentManhourMap[gradeStepCategoryKey] {
				userName := DepartmentManhourMap[gradeStepCategoryKey][i].UserName
				// 资产的工时已经消耗完了
				if tempAssetHour.LessThanOrEqual(emptyDecimal) {
					break
				}
				if userWorkhourMap[userName].LessThanOrEqual(emptyDecimal) {
					// 人员的工时被扣完了
					continue
				}
				// 扣除人员工时
				if userWorkhourMap[userName].GreaterThan(tempAssetHour) {
					tempDecrease, _ = tempAssetHour.Div(workhourProp).Float64()
					userWorkhourMap[userName] = userWorkhourMap[userName].Sub(tempAssetHour)
					workHourTotal = workHourTotal.Sub(tempAssetHour)
					tempAssetHour = emptyDecimal
				} else {
					tempDecrease, _ = userWorkhourMap[userName].Div(workhourProp).Float64()
					tempAssetHour = tempAssetHour.Sub(userWorkhourMap[userName])
					workHourTotal = workHourTotal.Sub(userWorkhourMap[userName])
					userWorkhourMap[userName] = emptyDecimal
				}

				resKey := fmt.Sprintf("%d#%s", assetCount.EntityId, userName)
				if TaskType == "asset" {
					if _, ok := resMap[resKey]; !ok {
						resMap[resKey] = types.FinanceAssetHourCountResItem{
							ProjectName:          assetCount.ProjectName,
							AssetName:            assetCount.EntityName,
							AssetEpisodeCode:     assetCount.InitialEpisodeCode,
							DepartmentParentName: DepartmentManhourMap[gradeStepCategoryKey][i].DepartmentName,
							DepartmentName:       DepartmentManhourMap[gradeStepCategoryKey][i].GroupName,
							UserName:             DepartmentManhourMap[gradeStepCategoryKey][i].UserName,
							DingHours:            tempDecrease,
							RelatedEpisodeList:   assetCount.RelatedEpisodeList,
							EpisodeCost:          0,
						}
					} else {
						var tmpDingHour, _ = decimal.NewFromFloat(resMap[resKey].DingHours).Add(decimal.NewFromFloat(tempDecrease)).Float64()
						resMap[resKey] = types.FinanceAssetHourCountResItem{
							ProjectName:          assetCount.ProjectName,
							AssetName:            assetCount.EntityName,
							AssetEpisodeCode:     assetCount.InitialEpisodeCode,
							DepartmentParentName: DepartmentManhourMap[gradeStepCategoryKey][i].DepartmentName,
							DepartmentName:       DepartmentManhourMap[gradeStepCategoryKey][i].GroupName,
							UserName:             DepartmentManhourMap[gradeStepCategoryKey][i].UserName,
							DingHours:            tmpDingHour,
							RelatedEpisodeList:   assetCount.RelatedEpisodeList,
							EpisodeCost:          0,
						}
					}
				} else {
					res = append(res, types.FinanceAssetHourCountResItem{
						ProjectName:          assetCount.ProjectName,
						AssetName:            assetCount.EntityName,
						AssetEpisodeCode:     assetCount.InitialEpisodeCode,
						DepartmentParentName: DepartmentManhourMap[gradeStepCategoryKey][i].DepartmentName,
						DepartmentName:       DepartmentManhourMap[gradeStepCategoryKey][i].GroupName,
						UserName:             DepartmentManhourMap[gradeStepCategoryKey][i].UserName,
						DingHours:            Decimal(tempDecrease),
						RelatedEpisodeList:   assetCount.RelatedEpisodeList,
						EpisodeCost:          0,
					})
				}

			}
		}
		// 匹配完了还没匹配到工时
		if tempAssetHour.GreaterThan(emptyDecimal) {
			//println("没有在部门里匹配到足够的工时 去其他部门借")
			for DepartmentManhourIndex := range DepartmentManhourList {
				userName := DepartmentManhourList[DepartmentManhourIndex].UserName
				if tempAssetHour.LessThanOrEqual(emptyDecimal) {
					break
				}
				if userWorkhourMap[userName].LessThanOrEqual(emptyDecimal) {
					continue
				}
				workhourProp := f.getWorkhourProp(DepartmentManhourList[DepartmentManhourIndex].Grade)
				// 扣除人员工时
				if userWorkhourMap[userName].GreaterThanOrEqual(tempAssetHour) {
					tempDecrease, _ = tempAssetHour.Div(workhourProp).Float64()
					userWorkhourMap[userName] = userWorkhourMap[userName].Sub(tempAssetHour)
					workHourTotal = workHourTotal.Sub(tempAssetHour)
					tempAssetHour = emptyDecimal
				} else {
					tempDecrease, _ = userWorkhourMap[userName].Div(workhourProp).Float64()
					tempAssetHour = tempAssetHour.Sub(userWorkhourMap[userName])
					workHourTotal = workHourTotal.Sub(userWorkhourMap[userName])
					userWorkhourMap[userName] = emptyDecimal
				}
				if TaskType == "asset" {
					resKey := fmt.Sprintf("%d#%s", assetCount.EntityId, userName)
					if _, ok := resMap[resKey]; !ok {
						resMap[resKey] = types.FinanceAssetHourCountResItem{
							ProjectName:          assetCount.ProjectName,
							AssetName:            assetCount.EntityName,
							AssetEpisodeCode:     assetCount.InitialEpisodeCode,
							DepartmentParentName: DepartmentManhourList[DepartmentManhourIndex].DepartmentName,
							DepartmentName:       DepartmentManhourList[DepartmentManhourIndex].GroupName,
							UserName:             DepartmentManhourList[DepartmentManhourIndex].UserName,
							DingHours:            tempDecrease,
							RelatedEpisodeList:   assetCount.RelatedEpisodeList,
							EpisodeCost:          0,
						}
					} else {
						var tmpDingHour, _ = decimal.NewFromFloat(resMap[resKey].DingHours).Add(decimal.NewFromFloat(tempDecrease)).Float64()
						resMap[resKey] = types.FinanceAssetHourCountResItem{
							ProjectName:          assetCount.ProjectName,
							AssetName:            assetCount.EntityName,
							AssetEpisodeCode:     assetCount.InitialEpisodeCode,
							DepartmentParentName: DepartmentManhourList[DepartmentManhourIndex].DepartmentName,
							DepartmentName:       DepartmentManhourList[DepartmentManhourIndex].GroupName,
							UserName:             DepartmentManhourList[DepartmentManhourIndex].UserName,
							DingHours:            tmpDingHour,
							RelatedEpisodeList:   assetCount.RelatedEpisodeList,
							EpisodeCost:          0,
						}
					}
				} else {
					res = append(res, types.FinanceAssetHourCountResItem{
						ProjectName:          assetCount.ProjectName,
						AssetName:            assetCount.EntityName,
						AssetEpisodeCode:     assetCount.InitialEpisodeCode,
						DepartmentParentName: DepartmentManhourList[DepartmentManhourIndex].DepartmentName,
						DepartmentName:       DepartmentManhourList[DepartmentManhourIndex].GroupName,
						UserName:             DepartmentManhourList[DepartmentManhourIndex].UserName,
						DingHours:            Decimal(tempDecrease),
						RelatedEpisodeList:   assetCount.RelatedEpisodeList,
						EpisodeCost:          0,
					})
				}
			}
		}
	}
	if TaskType == "asset" {
		// 资产合并数据 资产#用户
		for _, item := range resMap {
			item.DingHours = Decimal(item.DingHours)
			res = append(res, item)
		}
		sort.Sort(res)
	}

	return
}

func (f *Finance) GetEpisodeStepCategoryPlanProportion(tenantId int64, from uint64, to uint64, categoryIdList []uint64) (res map[string]decimal.Decimal, err error) {

	var planData []types.EpisodeCodeStepCategoryPlanItem

	err = ClickHouse.DB.Table("plan p").
		Joins("JOIN entity e on e.id = p.entity_id").
		Where("p.tenant_id = ? and p.start_time < ? and p.end_time > ? and p.step_category_id in ? and p.end_time - p.start_time > 0", tenantId, from, to, categoryIdList).
		Select("e.code as episode_code,p.step_category_id as step_category_id,p.start_time,p.end_time").Find(&planData).Error
	if err != nil {
		return
	}

	var realStartTime, realEndTime decimal.Decimal
	res = make(map[string]decimal.Decimal, 0)
	for _, datum := range planData {
		if datum.StartTime > from {
			realStartTime = decimal.NewFromInt(int64(from))
		} else {
			realStartTime = decimal.NewFromInt(int64(datum.StartTime))
		}

		if datum.EndTime < to {
			realEndTime = decimal.NewFromInt(int64(datum.EndTime))
		} else {
			realEndTime = decimal.NewFromInt(int64(to))
		}
		res[fmt.Sprintf("%s#%d", datum.EpisodeCode, datum.StepCategoryId)] = realEndTime.Sub(realStartTime).DivRound(decimal.NewFromInt(int64(datum.EndTime-datum.StartTime)), 2)
	}

	return
}

// GetAssetRelatedEpisodeList 获得资产关联的剧集
func (f *Finance) GetAssetRelatedEpisodeList(assetIdList []uint64) (res types.AssetRelatedEpisodeListRes, err error) {

	var assetRelatedEpisodeList []types.AssetRelatedEpisodeList
	err = ClickHouse.DB.Table("(?) as m", ClickHouse.DB.Table("module m").Joins("JOIN entity e on e.module_id = m.id").
		Where("e.module_id = (select id from module where code = 'shot') and (JSONExtractString(e.json, concat(m.code, '_asset')) <> ''"+
			"   or JSONExtractString(e.json, concat(m.code, '_level')) <> '')").
		Select("toUInt64OrZero(arrayJoin(arrayConcat(splitByChar(',', ifNull(JSONExtractString(e.json, concat(m.code, '_asset')), '')),"+
			"splitByChar(',',ifNull(JSONExtractString(e.json, concat(m.code, '_level')), '')))  )) as id,"+
			"arrayStringConcat(groupUniqArray(e.initial_episode_code),',') as episode_code_str").
		Group("id")).Where("id in ?", assetIdList).Select("id as entity_id,episode_code_str").Find(&assetRelatedEpisodeList).Error
	if err != nil {
		return
	}
	res = make(types.AssetRelatedEpisodeListRes, 0)
	for _, list := range assetRelatedEpisodeList {
		res[list.EntityId] = list
	}

	return
}

// FinanceAssetHourCount
func (f *Finance) FinanceAssetHourCountV2(tenantId int64, TaskType string, month string, projectIdList []uint64, episodeList []types.EpisodeItem) (res types.FinanceAssetHourCountRes, err error) {

	monthTime, err := time.ParseInLocation("2006-01", month, time.Local)
	if err != nil {
		return
	}
	from := GetFirstDayOfMonth(monthTime)
	to := GetLastDayOfMonth(monthTime)
	// 通过时间 获取项目计划
	var moduleCodeList []string
	if TaskType == "asset" {
		moduleCodeList = []string{"level", "asset", "design"}
	} else {
		moduleCodeList = []string{"shot", "sequence"}
	}
	assetStepCategory, err := f.GetModulesStepCategory(moduleCodeList)
	if err != nil {
		return
	}
	var assetStepCategoryIdList []uint64
	for _, category := range assetStepCategory {
		assetStepCategoryIdList = append(assetStepCategoryIdList, category.Id)
	}

	// 根据项目id获取项目对应月份计划
	var planWhere = ClickHouse.DB.Where("p.tenant_id = ? and p.type = 'step_category' and p.end_time > ? and p.start_time < ? and p.step_category_id in ? and p.end_time - p.start_time > 0", tenantId, from.Unix(), to.Unix(), assetStepCategoryIdList)
	if len(projectIdList) > 0 {
		planWhere = planWhere.Where("p.project_id in ?", projectIdList)
	}

	episodeStepCategoryList, err := f.GetEpisodeStepCategoryByPlan(planWhere)
	if len(episodeStepCategoryList) == 0 {
		return
	}

	// 计划时间比例
	planTimeProportion, err := f.GetEpisodeStepCategoryPlanProportion(tenantId, uint64(from.Unix()), uint64(to.Unix()), assetStepCategoryIdList)
	if err != nil {
		return
	}

	var assetWhere = ClickHouse.DB
	for _, item := range episodeStepCategoryList {
		assetWhere = assetWhere.Or("entity.initial_episode_code in ? and entity.project_id = ? and task.step_category_id = ?", strings.Split(item.EpisodeCodeStr, ","), item.ProjectId, item.StepCategoryId)
	}
	if len(projectIdList) > 0 {
		assetWhere = assetWhere.Where("entity.project_id in ?", projectIdList)
	}

	if len(episodeList) > 0 {
		var assetEpisodeWhere = ClickHouse.DB
		for _, item := range episodeList {
			assetEpisodeWhere.Or("entity.project_id = ? and entity.initial_episode_code in ?", item.ProjectId, item.EpisodeCodeList)
		}
		assetWhere = assetWhere.Where(assetEpisodeWhere)
	}

	// 计算资产工时
	var assetDepartmentRes []types.EntityDepartmentStepCategoryHourCount
	err = ClickHouse.DB.Model(&model.Module{}).
		Joins("JOIN entity on entity.module_id = module.id").
		Joins("JOIN task on task.entity_id = entity.id").
		Joins("JOIN user on user.id = task.executor").
		Where(assetWhere).
		Where("task.step_type = 'step' and task.department_id > 0").
		//Group("entity.project_id,entity.id,entity.name,grade,entity.initial_episode_code,task.step_category_id,task.department_id").
		Group("entity.project_id,entity.id,entity.initial_episode_code,task.step_category_id").
		//Select("entity.project_id AS project_id,entity.id as entity_id,entity.name as entity_name,JSONExtractString(entity.json, concat(module.code, '_grade')) AS grade,entity.initial_episode_code AS initial_episode_code,task.step_category_id as step_category_id,task.department_id AS department_id,sum(toUInt64OrZero(toString(toFloat64OrZero(task.man_hour) * 100)))/100 AS hours").
		Select("entity.project_id AS project_id,entity.id as entity_id,entity.initial_episode_code AS initial_episode_code,task.step_category_id as step_category_id,sum(toUInt64OrZero(toString(toFloat64OrZero(task.man_hour) * 100)))/100 AS hours").
		Having("hours > 0").
		Find(&assetDepartmentRes).Error
	if err != nil {
		return
	}
	if len(assetDepartmentRes) == 0 {
		return
	}

	var assetResMap = make(map[string]types.EntityDepartmentHourCount, 0)
	var planProportion = decimal.NewFromFloat(0.00)
	var entityStepCategoryKey = ""
	var assetIdMap = make(map[uint64]uint64, 0)
	var assetIdList []uint64
	var projectIdMap = make(map[uint64]uint64, 0)
	var projectIdListForQuery []uint64
	var stepCategoryIdList []uint64
	var stepCategoryIdMap = make(map[uint64]uint64)
	for _, re := range assetDepartmentRes {
		if _, ok := assetIdMap[re.EntityId]; !ok {
			assetIdList = append(assetIdList, re.EntityId)
			assetIdMap[re.EntityId] = re.EntityId
		}
		if _, ok := projectIdMap[re.ProjectId]; !ok {
			projectIdListForQuery = append(projectIdListForQuery, re.ProjectId)
			projectIdMap[re.ProjectId] = re.ProjectId
		}
		if _, ok := stepCategoryIdMap[re.StepCategoryId]; !ok {
			stepCategoryIdList = append(stepCategoryIdList, re.StepCategoryId)
			stepCategoryIdMap[re.StepCategoryId] = re.StepCategoryId
		}

		entityStepCategoryKey = fmt.Sprintf("%d#%d", re.StepCategoryId, re.EntityId)
		planProportion = decimal.NewFromInt(1)
		if _, ok := planTimeProportion[fmt.Sprintf("%s#%d", re.InitialEpisodeCode, re.StepCategoryId)]; ok {
			planProportion = planTimeProportion[fmt.Sprintf("%s#%d", re.InitialEpisodeCode, re.StepCategoryId)]
		}
		if _, ok := assetResMap[entityStepCategoryKey]; !ok {
			assetResMap[entityStepCategoryKey] = types.EntityDepartmentHourCount{
				ProjectId:          re.ProjectId,
				StepCategoryId:     re.StepCategoryId,
				InitialEpisodeCode: re.InitialEpisodeCode,
				EntityName:         re.EntityName,
				Grade:              re.Grade,
				EntityId:           re.EntityId,
				Hours:              re.Hours,
				HoursDecimal:       decimal.NewFromFloat(re.Hours).Mul(planProportion),
				Proportion:         planProportion,
			}
		} else {
			assetResMap[entityStepCategoryKey] = types.EntityDepartmentHourCount{
				ProjectId:          re.ProjectId,
				StepCategoryId:     re.StepCategoryId,
				InitialEpisodeCode: re.InitialEpisodeCode,
				EntityName:         re.EntityName,
				Grade:              re.Grade,
				EntityId:           re.EntityId,
				Hours:              re.Hours + assetResMap[entityStepCategoryKey].Hours,
				HoursDecimal:       decimal.NewFromFloat(re.Hours).Mul(planProportion).Add(assetResMap[entityStepCategoryKey].HoursDecimal),
				Proportion:         planProportion,
			}
		}
	}

	// 查询实体信息
	entityDataMap, err := f.GetAssetDataMap(ClickHouse.DB.Where("entity.id in ?", assetIdList))
	if err != nil {
		return
	}
	// 查询项目信息
	var projectService = Project{}
	projectData, err := projectService.GetProjectNameMapByIds(projectIdListForQuery)
	if err != nil {
		return
	}
	var assetEpisodeRelation types.AssetRelatedEpisodeListRes
	if TaskType == "asset" {
		// 查询资产出现的集
		assetEpisodeRelation, err = f.GetAssetRelatedEpisodeList(assetIdList)
		if err != nil {
			return
		}
	}

	standardHours := decimal.NewFromInt(0)
	departmentIdMap := make(map[uint64]uint64, 0)
	var departmentIdList []uint64

	// 字典转数组 字典是没办法修改字典element里数据的 因为map里的element 如果为map型是不可寻址的
	var assetRes types.EntityDepartmentHourCountList
	for _, assetCountItem := range assetResMap {
		standardHours = standardHours.Add(assetCountItem.HoursDecimal)
		if _, ok := departmentIdMap[assetCountItem.DepartmentId]; !ok {
			departmentIdList = append(departmentIdList, assetCountItem.DepartmentId)
		}
		departmentIdMap[assetCountItem.DepartmentId] = assetCountItem.DepartmentId
		if TaskType == "asset" {
			if _, ok := assetEpisodeRelation[assetCountItem.EntityId]; ok {
				assetCountItem.RelatedEpisodeList = strings.Split(assetEpisodeRelation[assetCountItem.EntityId].EpisodeCodeStr, ",")
			}
		}
		if _, ok := entityDataMap[assetCountItem.EntityId]; ok {
			assetCountItem.EntityName = entityDataMap[assetCountItem.EntityId].Name
			assetCountItem.Grade = entityDataMap[assetCountItem.EntityId].AssetGrade
		}
		if _, ok := projectData[assetCountItem.ProjectId]; ok {
			assetCountItem.ProjectName = projectData[assetCountItem.ProjectId]
		}

		assetRes = append(assetRes, assetCountItem)
	}

	// 按照等级排序
	sort.Sort(assetRes)

	var userMonthWorkhourService UserMonthWorkhour
	var workHourWhere = ClickHouse.DB.Where("umw.department_name <> '导演' and umw.is_leave <> 'yes' and umw.is_change_position <> 'yes' and tu.tenant_id = ? and umw.work_date between ? and ? and r.code in ?", tenantId, from.Unix(), to.Unix(), []string{"headman", "artist"})
	if TaskType == "shot" {
		workHourWhere = workHourWhere.Where("umw.name <> '阮向彬'")
	}
	DepartmentManhourList, err := userMonthWorkhourService.GetMonthManhour(workHourWhere, stepCategoryIdList)
	if err != nil {
		return
	}
	// 离职人员工时查询
	LeavePeopleDepartmentManhourList, err := userMonthWorkhourService.GetMonthManhourLeavePeople(ClickHouse.DB.Where("umw.department_name <> '导演' and umw.is_leave = 'yes' and umw.work_date between ? and ? ", from.Unix(), to.Unix()), stepCategoryIdList)
	if err != nil {
		return
	}
	if len(LeavePeopleDepartmentManhourList) > 0 {
		DepartmentManhourList = append(DepartmentManhourList, LeavePeopleDepartmentManhourList...)
	}
	// 换岗人员工时查询
	changePositionPeopleDepartmentManhourList, err := userMonthWorkhourService.GetMonthManhourLeavePeople(ClickHouse.DB.Where("umw.department_name <> '导演' and umw.is_change_position = 'yes' and umw.work_date between ? and ? ", from.Unix(), to.Unix()), stepCategoryIdList)
	if err != nil {
		return
	}
	if len(changePositionPeopleDepartmentManhourList) > 0 {
		DepartmentManhourList = append(DepartmentManhourList, changePositionPeopleDepartmentManhourList...)
	}
	DepartmentManhourMap := make(map[string][]*types.DepartmentUserDingManhour)
	userWorkhourMap := make(map[string]decimal.Decimal)
	workHourTotal := decimal.NewFromInt(0)

	for i, manhour := range DepartmentManhourList {
		workhourProp := f.getWorkhourProp(manhour.Grade)
		DepartmentManhourList[i].HoursDecimal = decimal.NewFromFloat(manhour.Hours).Mul(workhourProp)
		gradeStepCategoryKey := fmt.Sprintf("%d#%s", manhour.StepCategoryId, manhour.Grade)
		DepartmentManhourMap[gradeStepCategoryKey] = append(DepartmentManhourMap[gradeStepCategoryKey], &DepartmentManhourList[i])
		if _, ok := userWorkhourMap[manhour.UserName]; !ok {
			workHourTotal = workHourTotal.Add(DepartmentManhourList[i].HoursDecimal)
		}
		userWorkhourMap[manhour.UserName] = DepartmentManhourList[i].HoursDecimal
	}

	gradeStepCategoryKey := ""
	var tempAssetHour decimal.Decimal
	hourProportion := workHourTotal.DivRound(standardHours, 8) // 工时比例
	emptyDecimal := decimal.NewFromInt(0)
	resMap := make(map[string]types.FinanceAssetHourCountResItem, 0)
	for _, assetCount := range assetRes {
		if assetCount.HoursDecimal.Equal(emptyDecimal) {
			//println("资产工时为0")
			continue
		}
		tempAssetHour = assetCount.HoursDecimal.Mul(hourProportion)
		var tempDecrease float64
		for _, grade := range gradeList {
			gradeStepCategoryKey = fmt.Sprintf("%d#%s", assetCount.StepCategoryId, grade)
			if _, ok := DepartmentManhourMap[gradeStepCategoryKey]; !ok {
				// 找不到对应等级的出勤人员
				//println("找不到对应等级的出勤人员", gradeStepCategoryKey)
				continue
			}
			workhourProp := f.getWorkhourProp(grade)
			// 找到对应等级人员 查看工时是否足够
			for i := range DepartmentManhourMap[gradeStepCategoryKey] {
				userName := DepartmentManhourMap[gradeStepCategoryKey][i].UserName
				// 资产的工时已经消耗完了
				if tempAssetHour.LessThanOrEqual(emptyDecimal) {
					break
				}
				if userWorkhourMap[userName].LessThanOrEqual(emptyDecimal) {
					// 人员的工时被扣完了
					continue
				}
				// 扣除人员工时
				if userWorkhourMap[userName].GreaterThan(tempAssetHour) {
					tempDecrease, _ = tempAssetHour.Div(workhourProp).Float64()
					userWorkhourMap[userName] = userWorkhourMap[userName].Sub(tempAssetHour)
					workHourTotal = workHourTotal.Sub(tempAssetHour)
					tempAssetHour = emptyDecimal
				} else {
					tempDecrease, _ = userWorkhourMap[userName].Div(workhourProp).Float64()
					tempAssetHour = tempAssetHour.Sub(userWorkhourMap[userName])
					workHourTotal = workHourTotal.Sub(userWorkhourMap[userName])
					userWorkhourMap[userName] = emptyDecimal
				}

				resKey := fmt.Sprintf("%d#%s", assetCount.EntityId, userName)
				if TaskType == "asset" {
					if _, ok := resMap[resKey]; !ok {
						resMap[resKey] = types.FinanceAssetHourCountResItem{
							ProjectName:          assetCount.ProjectName,
							AssetName:            assetCount.EntityName,
							AssetEpisodeCode:     assetCount.InitialEpisodeCode,
							AssetGrade:           assetCount.Grade,
							DepartmentParentName: DepartmentManhourMap[gradeStepCategoryKey][i].DepartmentName,
							DepartmentName:       DepartmentManhourMap[gradeStepCategoryKey][i].GroupName,
							UserName:             DepartmentManhourMap[gradeStepCategoryKey][i].UserName,
							DingHours:            tempDecrease,
							RelatedEpisodeList:   assetCount.RelatedEpisodeList,
							EpisodeCost:          0,
						}
					} else {
						var tmpDingHour, _ = decimal.NewFromFloat(resMap[resKey].DingHours).Add(decimal.NewFromFloat(tempDecrease)).Float64()
						resMap[resKey] = types.FinanceAssetHourCountResItem{
							ProjectName:          assetCount.ProjectName,
							AssetName:            assetCount.EntityName,
							AssetEpisodeCode:     assetCount.InitialEpisodeCode,
							AssetGrade:           assetCount.Grade,
							DepartmentParentName: DepartmentManhourMap[gradeStepCategoryKey][i].DepartmentName,
							DepartmentName:       DepartmentManhourMap[gradeStepCategoryKey][i].GroupName,
							UserName:             DepartmentManhourMap[gradeStepCategoryKey][i].UserName,
							DingHours:            tmpDingHour,
							RelatedEpisodeList:   assetCount.RelatedEpisodeList,
							EpisodeCost:          0,
						}
					}
				} else {
					res = append(res, types.FinanceAssetHourCountResItem{
						ProjectName:          assetCount.ProjectName,
						AssetName:            assetCount.EntityName,
						AssetEpisodeCode:     assetCount.InitialEpisodeCode,
						AssetGrade:           assetCount.Grade,
						DepartmentParentName: DepartmentManhourMap[gradeStepCategoryKey][i].DepartmentName,
						DepartmentName:       DepartmentManhourMap[gradeStepCategoryKey][i].GroupName,
						UserName:             DepartmentManhourMap[gradeStepCategoryKey][i].UserName,
						DingHours:            Decimal(tempDecrease),
						RelatedEpisodeList:   assetCount.RelatedEpisodeList,
						EpisodeCost:          0,
					})
				}

			}
		}
		// 匹配完了还没匹配到工时
		if tempAssetHour.GreaterThan(emptyDecimal) {
			//println("没有在部门里匹配到足够的工时 去其他部门借")
			for DepartmentManhourIndex := range DepartmentManhourList {
				userName := DepartmentManhourList[DepartmentManhourIndex].UserName
				if tempAssetHour.LessThanOrEqual(emptyDecimal) {
					break
				}
				if userWorkhourMap[userName].LessThanOrEqual(emptyDecimal) {
					continue
				}
				workhourProp := f.getWorkhourProp(DepartmentManhourList[DepartmentManhourIndex].Grade)
				// 扣除人员工时
				if userWorkhourMap[userName].GreaterThanOrEqual(tempAssetHour) {
					tempDecrease, _ = tempAssetHour.Div(workhourProp).Float64()
					userWorkhourMap[userName] = userWorkhourMap[userName].Sub(tempAssetHour)
					workHourTotal = workHourTotal.Sub(tempAssetHour)
					tempAssetHour = emptyDecimal
				} else {
					tempDecrease, _ = userWorkhourMap[userName].Div(workhourProp).Float64()
					tempAssetHour = tempAssetHour.Sub(userWorkhourMap[userName])
					workHourTotal = workHourTotal.Sub(userWorkhourMap[userName])
					userWorkhourMap[userName] = emptyDecimal
				}
				if TaskType == "asset" {
					resKey := fmt.Sprintf("%d#%s", assetCount.EntityId, userName)
					if _, ok := resMap[resKey]; !ok {
						resMap[resKey] = types.FinanceAssetHourCountResItem{
							ProjectName:          assetCount.ProjectName,
							AssetName:            assetCount.EntityName,
							AssetEpisodeCode:     assetCount.InitialEpisodeCode,
							AssetGrade:           assetCount.Grade,
							DepartmentParentName: DepartmentManhourList[DepartmentManhourIndex].DepartmentName,
							DepartmentName:       DepartmentManhourList[DepartmentManhourIndex].GroupName,
							UserName:             DepartmentManhourList[DepartmentManhourIndex].UserName,
							DingHours:            tempDecrease,
							RelatedEpisodeList:   assetCount.RelatedEpisodeList,
							EpisodeCost:          0,
						}
					} else {
						var tmpDingHour, _ = decimal.NewFromFloat(resMap[resKey].DingHours).Add(decimal.NewFromFloat(tempDecrease)).Float64()
						resMap[resKey] = types.FinanceAssetHourCountResItem{
							ProjectName:          assetCount.ProjectName,
							AssetName:            assetCount.EntityName,
							AssetEpisodeCode:     assetCount.InitialEpisodeCode,
							AssetGrade:           assetCount.Grade,
							DepartmentParentName: DepartmentManhourList[DepartmentManhourIndex].DepartmentName,
							DepartmentName:       DepartmentManhourList[DepartmentManhourIndex].GroupName,
							UserName:             DepartmentManhourList[DepartmentManhourIndex].UserName,
							DingHours:            tmpDingHour,
							RelatedEpisodeList:   assetCount.RelatedEpisodeList,
							EpisodeCost:          0,
						}
					}
				} else {
					res = append(res, types.FinanceAssetHourCountResItem{
						ProjectName:          assetCount.ProjectName,
						AssetName:            assetCount.EntityName,
						AssetEpisodeCode:     assetCount.InitialEpisodeCode,
						AssetGrade:           assetCount.Grade,
						DepartmentParentName: DepartmentManhourList[DepartmentManhourIndex].DepartmentName,
						DepartmentName:       DepartmentManhourList[DepartmentManhourIndex].GroupName,
						UserName:             DepartmentManhourList[DepartmentManhourIndex].UserName,
						DingHours:            Decimal(tempDecrease),
						RelatedEpisodeList:   assetCount.RelatedEpisodeList,
						EpisodeCost:          0,
					})
				}
			}
		}
		//if tempAssetHour.GreaterThan(emptyDecimal) {
		//	s, _ := tempAssetHour.Value()
		//	println("工时还没有匹配完", s)
		//}
	}
	if TaskType == "asset" {
		// 资产合并数据 资产#用户
		for _, item := range resMap {
			item.DingHours = Decimal(item.DingHours)
			res = append(res, item)
		}
		sort.Sort(res)
	}

	return
}

// FinanceAssetHourCountIsLocked 判断是否已经锁定
func (f *Finance) FinanceAssetHourCountIsLocked(month string, taskType string) (exist bool, err error) {
	var existCheck = model.FinanceMonthCountHistory{}
	err = ClickHouse.DB.Model(&model.FinanceMonthCountHistory{}).Where("task_type = ? and work_date = ?", taskType, month).First(&existCheck).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return false, nil
		}
		return false, err
	} else {
		return true, nil
	}
}

// FinanceAssetHourCountLockRecord 锁定工时报表
func (f *Finance) FinanceAssetHourCountLockRecord(tenantId int64, TaskType string, month string, projectIdList []uint64, episodeList []types.EpisodeItem) (err error) {
	locked, err := f.FinanceAssetHourCountIsLocked(month, TaskType)
	if err != nil || locked {
		return
	}
	res, err := f.FinanceAssetHourCountV2(tenantId, TaskType, month, projectIdList, episodeList)
	if err != nil {
		return
	}
	var dataSet = make([]model.FinanceMonthCountHistory, len(res))
	for i, re := range res {
		dataSet[i].WorkDate = month
		dataSet[i].ProjectName = re.ProjectName
		dataSet[i].AssetName = re.AssetName
		dataSet[i].AssetEpisodeCode = re.AssetEpisodeCode
		dataSet[i].AssetGrade = re.AssetGrade
		dataSet[i].DepartmentParentName = re.DepartmentParentName
		dataSet[i].DepartmentName = re.DepartmentName
		dataSet[i].UserName = re.UserName
		dataSet[i].DingHours = re.DingHours
		dataSet[i].RelatedEpisodeList = strings.Join(re.RelatedEpisodeList, ",")
		dataSet[i].EpisodeCost = re.EpisodeCost
		dataSet[i].TaskType = TaskType
	}
	if len(dataSet) > 0 {
		var financeMonthCountHistoryService = FinanceMonthCountHistory{}
		err = financeMonthCountHistoryService.AddAll(dataSet)
	}
	return
}

// FinanceAssetHourCountByRecord 财务工时统计 通过历史记录查询
func (f *Finance) FinanceAssetHourCountByRecord(taskType, month string) (res types.FinanceAssetHourCountRes, err error) {
	var financeMonthCountHistoryService = FinanceMonthCountHistory{}
	fh, err := financeMonthCountHistoryService.GetList(taskType, month)
	if err != nil {
		return
	}
	res = make(types.FinanceAssetHourCountRes, len(fh))
	for i, history := range fh {
		res[i].ProjectName = history.ProjectName
		res[i].AssetName = history.AssetName
		res[i].AssetEpisodeCode = history.AssetEpisodeCode
		res[i].AssetGrade = history.AssetGrade
		res[i].DepartmentParentName = history.DepartmentParentName
		res[i].DepartmentName = history.DepartmentName
		res[i].UserName = history.UserName
		res[i].DingHours = history.DingHours

		for _, s := range strings.Split(history.RelatedEpisodeList, ",") {
			if s != "" {
				res[i].RelatedEpisodeList = append(res[i].RelatedEpisodeList, s)
			}
		}
		res[i].EpisodeCost = history.EpisodeCost
	}
	sort.Sort(res)
	return
}

// FinanceAssetHourCountV3
func (f *Finance) FinanceAssetHourCountV3(ctx *gin.Context, tenantId int64, TaskType string, month string,
	projectIdList []uint64, episodeList []types.EpisodeItem) (res types.FinanceAssetHourCountResV3, err error) {

	monthTime, err := time.ParseInLocation("2006-01", month, time.Local)
	if err != nil {
		return
	}
	from := GetFirstDayOfMonth(monthTime)
	to := GetLastDayOfMonth(monthTime).AddDate(0, 0, 1)

	startDate := from.Format("2006-01-02 15:04:05")
	endDate := to.Format("2006-01-02 15:04:05")

	// 通过时间 获取项目计划
	var moduleCodeList []string
	if TaskType == "asset" {
		moduleCodeList = []string{"level", "asset", "design"}
	} else {
		moduleCodeList = []string{"shot", "sequence"}
	}
	assetStepCategory, err := f.GetModulesStepCategory(moduleCodeList)
	if err != nil {
		return
	}
	var assetStepCategoryIdList []uint64
	for _, category := range assetStepCategory {
		assetStepCategoryIdList = append(assetStepCategoryIdList, category.Id)
	}
	var assetWhere = ClickHouse.DB.
		Where("task.step_category_id in ? and timelog.correct_end_time > 0 and timelog.correct_start_time >= ? and timelog.correct_end_time <= ?",
			assetStepCategoryIdList, from.Unix(), to.Unix())
	if len(projectIdList) > 0 {
		assetWhere = assetWhere.Where("entity.project_id in ?", projectIdList)
	}

	if len(episodeList) > 0 {
		var assetEpisodeWhere = ClickHouse.DB
		for _, item := range episodeList {
			assetEpisodeWhere.Or("entity.project_id = ? and entity.initial_episode_code in ?",
				item.ProjectId, item.EpisodeCodeList)
		}
		assetWhere = assetWhere.Where(assetEpisodeWhere)
	}

	// 计算资产工时
	var assetDepartmentRes []types.EntityDepartmentStepCategoryHourCountV3
	err = ClickHouse.DB.Model(&model.Module{}).
		Joins("JOIN entity on entity.module_id = module.id").
		Joins("JOIN task on task.entity_id = entity.id").
		Joins("JOIN user on user.id = task.executor").
		Joins("JOIN timelog on user.id = timelog.created_by and timelog.link_id = task.id").
		Where(assetWhere).
		Where("task.step_type = 'step' and task.department_id > 0").
		Group("entity.project_id,entity.id,entity.initial_episode_code,timelog.created_by").
		Select("entity.project_id AS project_id,entity.id as entity_id," +
			"entity.initial_episode_code AS initial_episode_code," +
			"sum(timelog.correct_end_time - timelog.correct_start_time) / 3600 AS hours,timelog.created_by as user_id").
		Having("hours > 0").
		Find(&assetDepartmentRes).Error
	if err != nil {
		return
	}
	if len(assetDepartmentRes) == 0 {
		return
	}

	var assetResMap = make(map[string]types.EntityDepartmentHourCountV3, 0)
	var entityUserKey = ""
	var assetIdMap = make(map[uint64]uint64, 0)
	var assetIdList []uint64
	var projectIdMap = make(map[uint64]uint64, 0)
	var projectIdListForQuery []uint64
	var stepCategoryIdList []uint64
	var stepCategoryIdMap = make(map[uint64]uint64)
	var userIdList = make([]uint64, 0)
	var userIdMap = make(map[uint64]bool, 0)
	for _, re := range assetDepartmentRes {
		if _, ok := userIdMap[re.EntityId]; !ok {
			userIdList = append(userIdList, re.UserId)
			userIdMap[re.UserId] = true
		}
		if _, ok := assetIdMap[re.EntityId]; !ok {
			assetIdList = append(assetIdList, re.EntityId)
			assetIdMap[re.EntityId] = re.EntityId
		}
		if _, ok := projectIdMap[re.ProjectId]; !ok {
			projectIdListForQuery = append(projectIdListForQuery, re.ProjectId)
			projectIdMap[re.ProjectId] = re.ProjectId
		}
		if _, ok := stepCategoryIdMap[re.StepCategoryId]; !ok {
			stepCategoryIdList = append(stepCategoryIdList, re.StepCategoryId)
			stepCategoryIdMap[re.StepCategoryId] = re.StepCategoryId
		}

		entityUserKey = fmt.Sprintf("%d#%d", re.UserId, re.EntityId)
		if _, ok := assetResMap[entityUserKey]; !ok {
			assetResMap[entityUserKey] = types.EntityDepartmentHourCountV3{
				ProjectId:          re.ProjectId,
				StepCategoryId:     re.StepCategoryId,
				InitialEpisodeCode: re.InitialEpisodeCode,
				EntityName:         re.EntityName,
				Grade:              re.Grade,
				EntityId:           re.EntityId,
				Hours:              re.Hours,
				HoursDecimal:       decimal.NewFromFloat(re.Hours),
				UserId:             re.UserId,
			}
		} else {
			assetResMap[entityUserKey] = types.EntityDepartmentHourCountV3{
				ProjectId:          re.ProjectId,
				StepCategoryId:     re.StepCategoryId,
				InitialEpisodeCode: re.InitialEpisodeCode,
				EntityName:         re.EntityName,
				Grade:              re.Grade,
				EntityId:           re.EntityId,
				Hours:              re.Hours + assetResMap[entityUserKey].Hours,
				HoursDecimal:       decimal.NewFromFloat(re.Hours).Add(assetResMap[entityUserKey].HoursDecimal),
				UserId:             re.UserId,
			}
		}
	}
	// 查询用户信息
	var userService User
	userInfoMap, err := userService.GetUserMapWithTwoLevelDepartment(tenantId, ClickHouse.DB.Where("user.id in ?", userIdList))

	var workHourService WorkHour
	imUserIdList, err := userService.GetUnionIdListByUserIdList(userIdList)
	if err != nil {
		return
	}
	userWorkHourInfo, err := workHourService.QueryUserWorkHour(imUserIdList, startDate, endDate, ctx)
	if err != nil {
		return
	}
	// 查询实体信息
	entityDataMap, err := f.GetAssetDataMap(ClickHouse.DB.Where("entity.id in ?", assetIdList))
	if err != nil {
		return
	}
	// 查询项目信息
	var projectService = Project{}
	projectData, err := projectService.GetProjectNameMapByIds(projectIdListForQuery)
	if err != nil {
		return
	}
	var assetEpisodeRelation types.AssetRelatedEpisodeListRes
	if TaskType == "asset" {
		// 查询资产出现的集
		assetEpisodeRelation, err = f.GetAssetRelatedEpisodeList(assetIdList)
		if err != nil {
			return
		}
	}

	standardHours := decimal.NewFromInt(0)
	departmentIdMap := make(map[uint64]uint64, 0)
	var departmentIdList []uint64

	// 字典转数组 字典是没办法修改字典element里数据的 因为map里的element 如果为map型是不可寻址的
	var assetRes types.EntityDepartmentHourCountListV3
	for _, assetCountItem := range assetResMap {
		standardHours = standardHours.Add(assetCountItem.HoursDecimal)
		if _, ok := departmentIdMap[assetCountItem.DepartmentId]; !ok {
			departmentIdList = append(departmentIdList, assetCountItem.DepartmentId)
		}
		departmentIdMap[assetCountItem.DepartmentId] = assetCountItem.DepartmentId
		if TaskType == "asset" {
			if _, ok := assetEpisodeRelation[assetCountItem.EntityId]; ok {
				assetCountItem.RelatedEpisodeList = strings.Split(assetEpisodeRelation[assetCountItem.EntityId].EpisodeCodeStr, ",")
			}
		}
		if _, ok := entityDataMap[assetCountItem.EntityId]; ok {
			assetCountItem.EntityName = entityDataMap[assetCountItem.EntityId].Name
			assetCountItem.Grade = entityDataMap[assetCountItem.EntityId].AssetGrade
		}
		if _, ok := projectData[assetCountItem.ProjectId]; ok {
			assetCountItem.ProjectName = projectData[assetCountItem.ProjectId]
		}

		assetRes = append(assetRes, assetCountItem)
	}

	// 按照等级排序
	sort.Sort(assetRes)

	//resMap := make(map[string]types.FinanceAssetHourCountResItemV3, 0)
	for _, assetCount := range assetRes {
		tempAssetHour, _ := assetCount.HoursDecimal.Float64()
		var currentUserInfo = types.UserListWithTwoLevelDepartment{}
		if _, ok := userInfoMap[assetCount.UserId]; !ok {
			continue
		}
		currentUserInfo = userInfoMap[assetCount.UserId]
		currentUserWorkHour := 0.00
		if _, ok := userWorkHourInfo[currentUserInfo.UnionId]; ok {
			currentUserWorkHour = userWorkHourInfo[currentUserInfo.UnionId]
		}

		res = append(res, types.FinanceAssetHourCountResItemV3{
			ProjectName:          assetCount.ProjectName,
			AssetName:            assetCount.EntityName,
			AssetEpisodeCode:     assetCount.InitialEpisodeCode,
			AssetGrade:           assetCount.Grade,
			DepartmentParentName: currentUserInfo.DepartmentParentName,
			DepartmentName:       currentUserInfo.DepartmentName,
			UserName:             currentUserInfo.Name,
			DingHours:            Decimal(tempAssetHour),
			RelatedEpisodeList:   assetCount.RelatedEpisodeList,
			EpisodeCost:          0,
			WorkHour:             currentUserWorkHour,
		})
	}

	return
}
